Windows API 教程(九) 网络编程 您所在的位置:网站首页 windows sockets网络编程源码下载 Windows API 教程(九) 网络编程

Windows API 教程(九) 网络编程

2024-07-14 13:05| 来源: 网络整理| 查看: 265

茵蒂克丝 基础概念 ip 地址 服务端与客户端 Socket 基础概念 头文件和库文件 常用函数 WSAStartup ( ) 函数 WSACleanup ( ) 函数 Socket ( ) 函数 colsesocket() 函数 sockaddr 和 sockaddr_in 结构体 bind( ) 函数 listen( ) 函数 accept( ) 函数 connect( ) 函数 send( ) 函数 recv( ) 函数 简单的 socket 通信示例 服务端(Server) 客户端(Client) 简单的端口扫描程序 hostent结构体 代码 函数列表 基本Socket函数 获取信息 DLL操作 基础概念 IP 地址

IP 是英文 Internet Protocol (网络之间互连的协议)的缩写,也就是为计算机网络相互连接进行通信而设计的协议。任一系统,只要遵守 IP协议就可以与因特网互连互通。

所谓IP地址就是给每个遵循tcp/ip协议连接在Internet上的主机分配的一个32bit地址。按照TCP/IP协议规定,IP地址用二进制来表示,每个IP地址长32bit,比特换算成字节,就是4个字节。为了方便人们的使用,IP地址经常被写成十进制的形式,中间使用符号 “.” 分开不同的字节,如:192.168.1.1。在因特网中,主机的标识便是它ip地址。

常见的获取服务器ip的方法是使用系统自带的ping命令。例,打开cmd输入: ping www.baidu.com

输出: ping_baidu

其中的 115.239.210.27 就是baidu服务器的地址了

服务端与客户端

服务端与客户端在计算机的世界里,凡是提供服务的一方我们称为服务端(Server),而接受服务的另一方我们称作客户端(Client)。

网站提供网页数据的服务,使用者接受网站所提供的数据服务,所以使用者在这里就是客户端,响应使用者要求的网站即称为服务端。

不过客户端及服务端的关系不见得一定建立在两台分开的机器上,同一台机器中也有这种主从关系的存在,提供服务的服务端及接受服务的客户端也有可能都在同一台机器上。

例如我们在提供网页的服务器上执行浏览器浏览本机所提供的网页,这样在同一台机器上就同时扮演服务端及客户端。

Socket

socket的英文原义是“孔”或“插座”。作为4BDS UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。

Scoket程勋分为服务端与客户端,服务端程序监听端口,等待客户端程序的连接。客户端程序发起连接,等待服务端的相应。客户端想要连接上服务端的话,需要 知道服务端的ip地址。

例如,客户想要访问百度网站的首页,通过浏览器访问http://www.baidu.com。浏览器发出请求之后,先是DNS服务器将百度的域名解析成ip地址之后,访问到ip地址为115.239.210.27服务器的80端口(http协议默认在80端口),请求发送后,百度的服务器作为响应将页面的源代码发送至客户端(通过浏览器右键->源代码,或者ctrl+u可以看到服务器返回的页面源代码),随后浏览器将源代码渲染成页面。这样用户就看到了百度网站的首页。

头文件和库文件

在windows系统上,当前的Windows Socket接口是Windows Sockets 2,所有接口函数都是有Ws2_32.dll导出。在进行程序设计时,祥光的数据类型、结构定义、函数声明等卫浴头文件winsock2.h中,编译时需要包含此文件,并且连接到库Ws2_32.lib。

常用函数

常见的socket函数

WSAStartup 初始化Ws2_32.lib库 WSACleanup 关闭Ws2_32.lib库 Socket 创建socket closesocket 关闭socket sockaddr和sockaddr_in 结构体socket地址 bind 绑定 listen 监听 accept 接收 connect 连接 send 发送 recv 获取返回 WSAStartup () 函数

WSAStartup函数的功能使加载Ws2_32.dll等Socket程序运行的环境。在程序初始化后,Socket程序运行所依赖的动态链接库不一定已经在家,WSAStartup保证了Socket 动态链接库的加载。

1 2 3 4 int WSAStartup( __in WORD wVersionRequested, __out LPWSADATA lpWSAData );

参数一 wVersionRequested Socket程序库的版本,一般使用MAKEWORD(2,2)宏生成。

参数二 lpWSAData 输出参数,指向 WSADATA 结构的指针,用于返回Socket库初始化的信息。

返回值 用来判断程序是否成功加载

WSACleanup () 函数

与 WSAStartup 相反, WSACleanup 释放Ws2_32.dll,函数无参数。

Socket () 函数

Socket函数的功能是建立一个绑定到制定协议和传输类型的Socket。

1 2 3 4 5 SOCKET WSAAPI socket( __in int af, __in int type, __in int protocol );

参数一 af address family的缩写,制定该Socket使用哪一种类型的网络地址。可以是IPv4(AF_INET),也可以是IPv6(AF_INET6)、蓝牙(AF_BTM)、NetBios(AF_NETBIOS)等。

参数二 type 设置传输类型,常见类型有 SOCK_STREAM、 SOCK_DGRAM、 SOCK_RAM、 SOCK_SEQPACKET 等。SOCK_STREAM 类型是基于连接的(TCP),所收的数据时数据流形式的,传输层的数据包已经经过处理。SOCK_DGRAM是基于报文(UDP)。如果制定为SOCK_RAM,那么可以建立原始套接字,所收到的数据是以数据包(包括包头)的形式存在的。

参数三 protocol 设置传输协议,常用的协议为 IPPROTO_TCP 和 IPPROTO_UDP。

colsesocket() 函数

与 socket 函数对应,用于关闭 socket

1 2 3 int WSAAPI closesocket(  __in SOCKET s  );

参数一 s 指定要关闭的socket

sockaddr 和 sockaddr_in 结构体

sockaddr、 SOCKADDR、 sockaddr_in、 SOCKADDR_IN 这 几个结构用于表示地址很端口。在IPv4下,这几个结构体是可以通用的。

1 2 3 4 5 6 7 8 9 10 typedef struct sockaddr {   #if (_WIN32_WINNT < 0x0600)     u_short sa_family; #else     ADDRESS_FAMILY sa_family; // Address family. #endif //(_WIN32_WINNT < 0x0600)       CHAR sa_data[14]; // Up to 14 bytes of direct address. } SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR; 1 2 3 4 5 6 7 8 9 10 11 12 typedef struct sockaddr_in {   #if(_WIN32_WINNT < 0x0600)     short sin_family; #else //(_WIN32_WINNT < 0x0600)     ADDRESS_FAMILY sin_family; #endif //(_WIN32_WINNT < 0x0600)       USHORT sin_port;     IN_ADDR sin_addr;     CHAR sin_zero[8]; } SOCKADDR_IN, *PSOCKADDR_IN; bind() 函数

服务器端,将Socket与网络地址和端口绑定起来,函数原型如下:

1 2 3 4 5 int WSAAPI bind( __in SOCKET s, __in_bcount(namelen) const struct sockaddr FAR * name, __in int namelen );

参数一 s 要绑定的socket

参数二 name 要绑定的网络地址结构体

参数三 namelen name参数所指向的结构体的大小

listen() 函数

设置 socket 的状态为监听,使客户端程序可以进行连接,函数原型:

1 2 3 4 int WSAAPI listen( __in SOCKET s, __in int backlog );

参数一 s 绑定的socket

参数二 backlog 指定最大的连接数

accept() 函数

接受客户端的连接

1 2 3 4 5 SOCKET WSAAPI accept( __in SOCKET s, __out_bcount_opt(*addrlen) struct sockaddr FAR * addr, __inout_opt int FAR * addrlen );

参数一 s 正在监听的socket,该 socket 使用 listen() 设置。

参数二 addr (可选) 指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。

参数三 addrlen (可选) 指针,指向存有addr地址长度的整形数。

返回: 成功:非负描述字 失败:-1

accept 默认会阻塞进程,直到有客户端建立连接后返回,它返回的是连接用的 socket 。如果 accept 成功返回,则服务器与客户已经正确建立连接了,此时服务器通过 accept 返回的 socket 来完成与客户的通信。

connect() 函数

connect函数的功能使与服务器建立连接。这个函数只能由客户端程序调用。

1 2 3 4 5 int WSAAPI connect( __in SOCKET s, __in_bcount(namelen) const struct sockaddr FAR * name, __in int namelen );

参数一 s 由 socket 函数建立的套接字。

参数二 name 指向sockaddr结构体指针,包括了所要连接的服务端的地址和端口等。

参数三 namelen sockaddr结构的长度

send() 函数

向连接的另一端发送数据,不论是客户还是服务器应用程序都用 send 函数来向 TCP 连接的另一端发送数据。

1 2 3 4 5 6 int WSAAPI send( __in SOCKET s, __in_bcount(len) const char FAR * buf, __in int len, __in int flags );

参数一 s 指定发送端socket描述符。

参数二 *buf 指明存放要发送的数据的缓冲区。

参数三 len 指明实际要发送的数据的字节数。

参数四 flags 指明发送的方法,常见宏MSG_DONTROUTE、MSG_OOB等,一般置0。

将*buf指向的字符串发送至客户端

recv() 函数

不论是客户还是服务器应用程序都用 recv 函数 从 TCP 连接的另一端接收数据。

1 2 3 4 5 6 int WSAAPI recv( __in SOCKET s, __out_bcount_part(len, return) __out_data_source(NETWORK) char FAR * buf, __in int len, __in int flags );

参数一 s 指定接收端socket描述符;

参数二 *buf 指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;

参数三 len  指明buf的长度;

参数四 flags 指明接收的方法,常见宏 MSG_DONTROUTE、 MSG_OOB 等,一般置0。 获取到客户端返回的字符串并将其写入到buf中

简单的 socket 通信示例 服务端(Server) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include #include #pragma comment(lib, "ws2_32.lib")   void main() {     int err; // 错误信息     int len;       char sendBuf[100]; // 发送至客户端的字符串     char recvBuf[100]; // 接受客户端返回的字符串       SOCKET sockServer;     // 服务端 Socket     SOCKADDR_IN addrServer;// 服务端地址     SOCKET sockClient;     // 客户端 Scoket     SOCKADDR_IN addrClient;// 客户端地址       WSADATA wsaData;       // winsock 结构体     WORD wVersionRequested;// winsock 的版本       // 配置 Windows Socket版本     wVersionRequested = MAKEWORD( 2, 2 );       // 初始化 Windows Socket     err = WSAStartup( wVersionRequested, &wsaData );       if ( err != 0 )     {         // 启动错误,程序结束         return;     }       /*      * 确认WinSock DLL支持2.2      * 请注意如果DLL支持的版本大于2.2至2.2      * 它仍然会返回wVersion2.2的版本      */       if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )     {         // 启动错误,程序结束         WSACleanup(); // 终止Winsock 2 DLL (Ws2_32.dll) 的使用         return;     }       // 定义服务器端socket     sockServer = socket(AF_INET, SOCK_STREAM, 0);       //  设置服务端 socket     addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 本机IP     addrServer.sin_family = AF_INET;                   // 协议类型是INET     addrServer.sin_port = htons(6000);                 // 绑定端口6000       // 将服务器端socket绑定在本地端口     bind(sockServer, (SOCKADDR *)&addrServer, sizeof(SOCKADDR));       // Listen 监听端口     listen(sockServer, 5); // 5 为等待连接数目       printf("服务器已启动:\n监听中...\n");       len = sizeof(SOCKADDR);       // accept 会阻塞进程,直到有客户端连接上来为止     sockClient = accept(sockServer, (SOCKADDR *)&addrClient, &len);     // 当客户端连接上来时, 拼接如下字符串            sprintf(sendBuf, "欢迎 ip: %s 的用户连接, 这里是 Lellansin 的服务器\n", inet_ntoa(addrClient.sin_addr));     // 向客户端发送字符串     send(sockClient, sendBuf, strlen(sendBuf) + 1, 0);     // 获取客户端返回的数据     recv(sockClient, recvBuf, 100, 0);     // 打印客户端返回的数据     printf("%s\n", recvBuf);     // 关闭socket     closesocket(sockClient);       getchar(); }

启动后防火墙有阻挡提示,这里接受就可以了。另外下图是原来博主测试时截的图,跟上面代码的输出部分稍稍有些区别。

tcp_server_test

客户端(Client) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include #include #pragma comment(lib, "ws2_32.lib")   void main() {     int err;     char *message;     char recvBuf[100];       SOCKET sockClient; // 客户端 Scoket     SOCKADDR_IN addrServer; // 服务端地址       WSADATA wsaData;     WORD wVersionRequested;       wVersionRequested = MAKEWORD( 2, 2 );       err = WSAStartup( wVersionRequested, &wsaData );       if ( err != 0 )     {         return;     }       if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )     {         // 启动错误,程序结束         WSACleanup( );         return;     }       // 新建客户端 scoket     sockClient = socket(AF_INET, SOCK_STREAM, 0);       // 定义要连接的服务端地址     addrServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  // 目标IP (127.0.0.1是本机地址)     addrServer.sin_family = AF_INET;                           // 协议类型是INET     addrServer.sin_port = htons(6000);                         // 连接端口1234       // 让 sockClient 连接到 服务端     connect(sockClient, (SOCKADDR *)&addrServer, sizeof(SOCKADDR));       // 从服务端获取数据     recv(sockClient, recvBuf, 100, 0);       // 打印数据     printf("%s\n", recvBuf);       message = "服务端你好~";       // 发送数据到服务端     send(sockClient, message, strlen(message) + 1, 0);       // 关闭socket     closesocket(sockClient);     WSACleanup();       getchar(); // 暂停 }

tcp_client_test

客户端与服务端更多通信,例子:《木马,你好!(二)最简单的木马》 简单的端口扫描程序 hostent结构体

host entry的缩写,该结构记录主机的信息,包括主机名、别名、地址类型、地址长度和地址列表。之所以主机的地址是一个列表的形式,原因是当一个主机有多个网络接口时会有多个地址。

1 2 3 4 5 6 7 8 struct hostent {     char FAR * h_name;              /* 主机正式名称 */     char FAR * FAR * h_aliases;     /* 主机别名 */     short h_addrtype;               /* 地址类型 */     short h_length;                 /* 地址长度 */     char FAR * FAR * h_addr_list;   /* 主机网络地址指针 */     #define h_addr h_addr_list[0]   /* 指向第一个地址 */ }; 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 #include #include #include #pragma comment(lib,"ws2_32.lib")   void Help( void );   int main(int argc, char *argv[]) {     WORD wVersion = MAKEWORD(2, 0);     WSADATA wsaData;       //sockaddr_in结构体     struct sockaddr_in sin;       int iFromPort;     int iToPort;     int iNowPort;     char *cHost;       HOSTENT *host_entry;     char *host_name;     char host_address[256];       SOCKET s;       int iOpenPort = 0;     int port[256], i;       if (argc != 4)     {         Help();         return -1;     }       iFromPort = atoi(argv[2]);     iToPort = atoi(argv[3]);     host_name = argv[1];       if (iFromPort > iToPort || iFromPort < 0 || iFromPort > 65535 || iToPort < 0 || iToPort > 65535)     {         printf("起始端口不能大于结束端口,且范围为:1--65535 ");         return 0;     }       if ( WSAStartup(wVersion , &wsaData) )     {         printf("初始化失败!");         return -1;     }       // 根据用户输入的域名来获取服务器主机信息     host_entry = gethostbyname(host_name);       if (host_entry != 0)     {         wsprintf( host_address, "%d.%d.%d.%d",             (host_entry->h_addr_list[0][0] & 0x00ff),             (host_entry->h_addr_list[0][1] & 0x00ff),             (host_entry->h_addr_list[0][2] & 0x00ff),             (host_entry->h_addr_list[0][3] & 0x00ff)             );         printf("\n主机名称:%s  主机地址:%s \n", host_name, host_address);     }       cHost = host_address;       printf("======= 开始扫描 ======= \n");       for (iNowPort = iFromPort; iNowPort


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有